package com.zmcn.fileTransfer.business.filetransfer.client;

import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.security.Key;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;

import net.sf.json.JSONArray;
import net.sf.json.JSONObject;

import com.zmcn.fileTransfer.business.filetransfer.service.impl.FileTransferServiceImpl;
import com.zmcn.fileTransfer.business.filetransfer.vo.BatchFileVO;
import com.zmcn.fileTransfer.business.filetransfer.vo.FileInfoVO;
import com.zmcn.fileTransfer.fw.task.MyTask;
import com.zmcn.fileTransfer.fw.task.MyTaskLogic;
import com.zmcn.fileTransfer.fw.util.AESCoder;
import com.zmcn.fileTransfer.fw.util.FileOP;
import com.zmcn.fileTransfer.fw.util.HttpNet;

/**
 * 
 * 处理一次文件下载同步任务
 * 
 * @author <b>欧建东<br/>
 *         Email:oyjd@163.com</b>
 * @version 1.0
 */
public class DownFile {
	/**
	 * 目标地址
	 */
	private String souUrl;
	/**
	 * 中转服务器地址
	 */
	private String centerUrl;
	/**
	 * 目标输出路径
	 */
	private String aimPath;
	/**
	 * 切片临时存放路径
	 */
	private String tempPath;
	/**
	 * 逻辑service
	 */
	private FileTransferServiceImpl service;
	/**
	 * 切片大小
	 */
	private int size;
	/**
	 * 线程开启数
	 */
	private int threadNum;
	/**
	 * 数据流密匙
	 */
	private Key key;

	/**
	 * 构造函数
	 * 
	 * @param souUrl
	 * @param centerUrl
	 * @param account
	 * @param pwd
	 * @param aimPath
	 * @param tempPath
	 * @param size
	 * @param threadNum
	 */
	public DownFile(String souUrl, String centerUrl, String account, String pwd, String aimPath, String tempPath, int size, int threadNum, Key key) {
		super();
		this.souUrl = souUrl;
		this.centerUrl = centerUrl;
		this.aimPath = aimPath;
		this.tempPath = tempPath;
		this.size = size;
		this.threadNum = threadNum;
		this.service = new FileTransferServiceImpl();
		this.key = key;
		//
		this.service.setAccount(account);
		this.service.setPwd(pwd);
		new File(aimPath).mkdirs();
		new File(tempPath).mkdirs();
	}

	/**
	 * 启动
	 */
	public void start() {
		System.out.println("********************启动同步文件任务********************");
		List<FileInfoVO> list = getFileInfo();
		for (FileInfoVO info : list) {
			try {
				String outFile = aimPath + File.separator + info.getFileName() + "." + info.getFileEx();
				File f = new File(outFile);
				if (!f.exists()) {
					System.out.println("==============>[" + info.getFileName() + "." + info.getFileEx() + "]准备下载");
					getFile(info);
				} else if (f.length() != info.getFileSize()) {
					System.out.println("==============>[" + info.getFileName() + "." + info.getFileEx() + "]文件大小不一致，重新下载");
					f.delete();
					getFile(info);
				} else {
					System.out.println("==============>[" + info.getFileName() + "." + info.getFileEx() + "]已经存在");
				}
			} catch (Exception err) {
				err.printStackTrace();
			}
		}
		System.out.println("********************结束同步文件任务********************");
	}

	/**
	 * 获取文件系统
	 * 
	 * @return
	 * @throws Exception
	 */
	private List<FileInfoVO> getFileInfo() {
		List<FileInfoVO> result = new ArrayList<FileInfoVO>();
		try {
			// 请求数据
			HttpNet hn = new HttpNet();
			Map<String, Object> para = new HashMap<String, Object>();
			para.put("token", service.getToken());
			para.put("souUrl", souUrl);
			String url = centerUrl + "/center!getFileInfo.action";
			System.out.println("==============>读取文件列表");
			byte[] b = hn.getBytesFromNet(url, para);
			String c = new String(b, "utf-8");

			// 合成数据
			JSONObject jobj = JSONObject.fromObject(c);
			if (0 == jobj.getInt("code")) {
				JSONArray array = jobj.getJSONArray("info");
				for (int i = 0; i < array.size(); i++) {
					JSONObject tmp = array.getJSONObject(i);
					FileInfoVO vo = new FileInfoVO();
					vo.setFileEx(tmp.getString("fileEx"));
					vo.setFileName(tmp.getString("fileName"));
					vo.setFileSize(tmp.getLong("fileSize"));
					result.add(vo);
				}
			}
		} catch (Exception err) {
			err.printStackTrace();
			System.out.println("==============>读取文件列表异常！" + err.getMessage());
		}
		return result;
	}

	/**
	 * 获取文件系统
	 * 
	 * @return
	 * @throws Exception
	 */
	private void getFile(FileInfoVO info) throws Exception {
		String name = info.getFileName() + "." + info.getFileEx();
		int num = service.splitFile(info.getFileSize(), size);
		if (1 == num) {
			String outFile = aimPath + File.separator + name;
			getOneFile(name, 0, outFile);
			System.out.println("--------->[" + name + "]成功下载");
		} else {
			List<BatchFileVO> paras = new ArrayList<BatchFileVO>();
			Long tail = info.getFileSize() % size;
			int endSize = tail.intValue() == 0 ? size : tail.intValue();
			for (int i = 1; i <= num; i++) {
				paras.add(new BatchFileVO(info.getFileName(), info.getFileEx(), i, i == num ? endSize : size));
			}
			final long beginTime = System.currentTimeMillis();
			final CountDownLatch latch = new CountDownLatch(1);
			new MyTask<BatchFileVO>(threadNum, paras, new MyTaskLogic<BatchFileVO>() {

				public void doLogic(int count, int pos, BatchFileVO vo) {
					String outFile = tempPath + File.separator + vo.getFileName() + ".temp" + vo.getBatch();
					String fileName = vo.getFileName() + "." + vo.getFileEx();
					getOneFile(fileName, vo.getBatch(), outFile);
				}

				public void finishAll(List<BatchFileVO> paras) {
					fusion(paras, beginTime);
					latch.countDown();
				}
			}).start();
			latch.await();
		}
	}

	/**
	 * 文件合成
	 * 
	 * @param tmp
	 */
	private void fusion(List<BatchFileVO> list, long beginTime) {
		String name = list.get(0).getFileName() + "." + list.get(0).getFileEx();
		String lastOutFile = aimPath + File.separator + name;
		System.out.println("--------->[" + name + "]准备合成，共" + list.size() + "个碎片");

		// 检测
		boolean flag = true;
		for (BatchFileVO vo : list) {
			String outFile = tempPath + File.separator + vo.getFileName() + ".temp" + vo.getBatch();
			File f = new File(outFile);
			if (!f.exists()) {
				System.out.println("失败[不存在]-->" + outFile);
				flag = false;
				break;
			}
			if (f.length() != vo.getSize()) {
				System.out.println("失败[文件大小不一致]-->" + outFile);
				f.delete();
				flag = false;
				break;
			}
		}
		System.out.println("--------->[" + name + "]检测碎片成功");

		// 合成
		if (flag) {
			try {
				OutputStream baos = new FileOutputStream(new File(lastOutFile), true);
				for (BatchFileVO vo : list) {
					String outFile = tempPath + File.separator + vo.getFileName() + ".temp" + vo.getBatch();
					byte[] b = FileOP.readFile(outFile);
					baos.write(b);
					baos.flush();
				}
				baos.close();
			} catch (Exception err) {
				err.printStackTrace();
				flag = false;
			}
		}
		// 删除临时文件
		if (flag) {
			for (BatchFileVO vo : list) {
				String outFile = tempPath + File.separator + vo.getFileName() + ".temp" + vo.getBatch();
				File f = new File(outFile);
				f.delete();
			}
		}
		System.out.println("--------->[" + name + "]成功合成！任务总耗时：" + (System.currentTimeMillis() - beginTime) / 1000 + "秒");
	}

	/**
	 * 下载获取一个文件
	 * 
	 * @param fileName
	 * @param batch
	 * @param aim
	 * @return
	 */
	private boolean getOneFile(String fileName, int batch, String aim) {
		boolean flag = false;
		try {
			HttpNet hn = new HttpNet();
			Map<String, Object> para = new HashMap<String, Object>();
			para.put("souUrl", souUrl);
			para.put("token", service.getToken());
			para.put("fileName", fileName);
			para.put("batch", batch);
			para.put("size", size);
			String url = centerUrl + "/center!readFile.action";
			byte[] b = hn.getBytesFromNet(url, para);
			b = AESCoder.decrypt(b, key);
			FileOP.writeFile(aim, b);
			flag = true;
		} catch (Exception err) {
			err.printStackTrace();
		}
		return flag;
	}
}
